import {mix, isString, clone} from '@antv/util'
import {getShapeName} from "@/draw/util/clazz";
import {Base64} from 'js-base64';
import {Modal, Input, message} from 'ant-design-vue';
import {save as saveRoute} from "../../api/mapRoute/"
import {save as saveMap} from "../../api/map"
import {Minio} from 'minio-js'
import axios from "axios";

class Command {

    constructor() {
        this._cfgs = this.getDefaultCfg();
        this.list = [];
        this.queue = [];
        this.id = "";
    }

    getDefaultCfg(){
        return {_command: {zoomDelta: .1, queue: [], current: 0, clipboard: []}}
    }

    get(key){
        return this._cfgs[key];
    }

    set(key, val){
        this._cfgs[key] = val;
    }

    initPlugin(graph){
        this.initCommands();
        graph.getCommands = () => {return this.get('_command').queue};
        graph.getCurrentCommand = () => {
            const c = this.get("_command");
            return c.queue[c.current - 1];
        }
        graph.executeCommand = (name, cfg) => {this.execute(name, graph, cfg)};
        graph.commandEnable = (name) => {return this.enable(name, graph)}
        graph.commandLocked = (name) => {return this.locked(name, graph)}
    }

    execute(name, graph, cfg){
        const cmd = mix({}, this[name], cfg);
        const manager = this.get("_command");
        if(cmd.enable(graph)){
            cmd.init();
            if(cmd.queue){
                manager.queue.splice(manager.current, manager.queue.length - manager.current, cmd);
                manager.current++;
            }
        }
        graph.emit("beforecommandexecute", {command: cmd});
        cmd.execute(graph);
        graph.emit("aftercommandexecute", {command: cmd});
        return cmd;
    }

    registerCommand(name, cfg){
        if(this[name]){
            mix(this[name], cfg);
        } else {
          const cmd = mix({}, {
              name: name,
              shortcutCodes: [],
              queue: true,
              executeTimes: 1,
              init(){},
              enable(){ return true},
              locked(){ return false},
              execute(graph){
                  this.snapShot = graph.save();
                  this.selectedItems = graph.get("selectedItems");
                  this.method && (isString(this.method) ? graph[this.method] : this.method(graph));
              },
              back(graph){
                  graph.read(this.snapShot);
                  graph.set('selectedItems', this.selectedItems);
              }
          }, cfg);
          this[name] = cmd;
          this.list.push(cmd);
        }
    }

    locked(name, graph){
        return this[name].locked(graph);
    }

    enable(name, graph){
        return this[name].enable(graph);
    }

    initCommands(){
        const cmdPlugin = this;
        cmdPlugin.registerCommand("add", {
            enable: function (){
              return this.type && this.addModel;
            },
            execute: function (graph){
                const item = graph.add(this.type, this.addModel);
                if(this.executeTimes === 1){
                    this.addId = item.get("id");
                }
            },
            back: function (graph){
                graph.remove(this.addId);
            }
        });
        cmdPlugin.registerCommand("update", {
            enable: function (){
                return this.itemId && this.updateModel;
            },
            execute: function (graph){
              const item = graph.findById(this.itemId);
              if(item){
                  if(this.executeTimes === 1){
                      this.originModel = mix({}, item.getModel());
                      graph.update(item, this.updateModel);
                  }
              }
            },
            back: function (graph){
                const item = graph.findById(this.itemId);
                graph.update(item, this.originModel);
            }
        });
        cmdPlugin.registerCommand('delete', {
            enable: function (graph){
                const mode = graph.getCurrentMode();
                const selectedItems = graph.get("selectedItems");
                return mode === 'edit' && selectedItems && selectedItems.length > 0;
            },
            method: function (graph){
                const selectedItems = graph.get("selectedItems");
                graph.emit("beforedelete", {items: selectedItems});
                if(selectedItems && selectedItems.length > 0){
                    selectedItems.forEach(i => {
                        const node = graph.findById(i);
                        if(node){
                            graph.remove(i);
                        }
                    })
                }
                graph.emit("afterdelete", {items: selectedItems});
            },
            shortcutCodes: ['Delete', 'Backspace']
        });

        cmdPlugin.registerCommand('redo', {
            queue: false,
            enable: function (graph){
                const mode = graph.getCurrentMode();
                const manager = cmdPlugin.get('_command');
                return mode === 'edit' && manager.current < manager.queue.length;
            },
            execute: function (graph){
                const manager = cmdPlugin.get('_command');
                const cmd = manager.queue[manager.current];
                cmd && cmd.execute(graph);
                manager.current++;
            },
            shortcutCodes: [['metaKey', 'shiftKey', 'z'], ['ctrlKey', 'shiftKey', 'z']]
        });
        cmdPlugin.registerCommand('undo', {
            queue: false,
            enable: function (graph){
                const mode = graph.getCurrentMode();
                const manager = cmdPlugin.get('_command');
                return mode === 'edit' && manager.current > 0;
            },
            execute: function (graph){
                const manager = cmdPlugin.get('_command');
                const cmd = manager.queue[manager.current-1];
                if(cmd){
                    cmd.executeTimes++;
                    cmd.back(graph);
                }
                manager.current--;
            },
            shortcutCodes: [['metaKey', 'z'], ['ctrlKey', 'z']]
        });
        cmdPlugin.registerCommand('copy', {
            queue: false,
            enable: function (graph){
                const mode = graph.getCurrentMode();
                const items = graph.get('selectedItems');
                return mode === 'edit' && items && items.length > 0;
            },
            execute: function (graph){
                const manager = cmdPlugin.get('_command');
                manager.clipboard = [];
                const items = graph.get('selectedItems');
                if(items && items.length > 0){
                    const item = graph.findById(items[0]);
                    if(item){
                        manager.clipboard.push({type: item.get('type'), model: item.getModel()});
                    }
                }
            },
            shortcutCodes: [['metaKey', 'z'], ['ctrlKey', 'z']]
        });
        cmdPlugin.registerCommand('paste', {
            enable: function (graph){
                const mode = graph.getCurrentMode();
                return mode === 'edit' && cmdPlugin.get('_command').clipboard.length > 0;
            },
            method: function (graph){
                const manager = cmdPlugin.get('_command');
                this.pasteData = clone(manager.clipboard[0]);
                const addModel = this.pasteData.model;
                addModel.x && (addModel.x += 10);
                addModel.y && (addModel.y += 10);
                const {clazz = 'line'} = addModel;
                const timestamp = new Date().getTime();
                const id = clazz + timestamp;
                addModel.id = id;
                const item = graph.add(this.pasteData.type, addModel);
                item.toFront();
            }
        });
        cmdPlugin.registerCommand('zoomIn', {
            queue: false,
            enable: function (graph){
               const zoom = graph.getZoom();
               const maxZoom = graph.get('maxZoom');
               const minZoom = graph.get('minZoom');
               return zoom <= maxZoom && zoom >= minZoom;
            },
            execute: function (graph){
                const manager = cmdPlugin.get('_command');
                console.log(manager);
                const maxZoom = graph.get('maxZoom');
                const zoom = graph.getZoom();
                this.originZoom = zoom;
                let currentZoom = zoom + manager.zoomDelta;
                if(currentZoom > maxZoom){
                    currentZoom = maxZoom;
                }
                graph.zoomTo(currentZoom);
            },
            back: function (graph){
                graph.zoomTo(this.originZoom);
            },
            shortcutCodes: [['metaKey', '='], ['ctrlKey', '=']]
        });
        cmdPlugin.registerCommand('zoomOut', {
            queue: false,
            enable: function (graph){
                const zoom = graph.getZoom();
                const maxZoom = graph.get('maxZoom');
                const minZoom = graph.get('minZoom');
                return zoom <= maxZoom && zoom >= minZoom;
            },
            execute: function (graph){
                const manager = cmdPlugin.get('_command');
                const minZoom = graph.get('minZoom');
                const zoom = graph.getZoom();
                this.originZoom = zoom;
                let currentZoom = zoom - manager.zoomDelta;
                if(currentZoom < minZoom){
                    currentZoom = minZoom;
                }
                graph.zoomTo(currentZoom);
            },
            back: function (graph){
                graph.zoomTo(this.originZoom);
            },
            shortcutCodes: [['metaKey', '-'], ['ctrlKey', '-']]
        });
        cmdPlugin.registerCommand('resetZoom', {
            queue: false,
            execute: function (graph){
                const zoom = graph.getZoom();
                this.originZoom = zoom;
                graph.zoomTo(1);
            },
            back: function (grahp){
                grahp.zoomTo(this.originZoom);
            }
        });
        cmdPlugin.registerCommand('autoFit', {
            queue: false,
            execute: function (graph){
                const zoom = graph.getZoom();
                this.originZoom = zoom;
                graph.fitView(5);
            },
            back: function (grahp){
                grahp.zoomTo(this.originZoom);
            }
        });
        cmdPlugin.registerCommand('toFront', {
            queue: false,
            enable: function (graph){
                const items = graph.get('selectedItems');
                return items && items.length > 0
            },
            execute: function (graph){
                const items = graph.get('selectedItems');
                if(items && items.length > 0){
                    const item = graph.findById(items[0]);
                    item.toFront();
                    graph.paint();
                }
            }
        });

        cmdPlugin.registerCommand('toBack', {
            queue: false,
            enable: function (graph){
                const items = graph.get('selectedItems');
                return items && items.length > 0
            },
            execute: function (graph){
                const items = graph.get('selectedItems');
                if(items && items.length > 0){
                    const item = graph.findById(items[0]);
                    item.toBack();
                    graph.paint();
                }
            }
        });

        cmdPlugin.registerCommand('upload', {
            queue: false,
            execute:function(graph){
                const input = document.createElement("input");
                input.type = "file";
                input.click();
                input.addEventListener('change', (e)=>{
                    const file = e.target.files[0];
                    // const reader = new FileReader();
                    const minioClient = new Minio.Client({
                        endPoint: '192.168.202.52',
                        port: 19000,
                        useSSL: false,
                        accessKey: 'minioadmin',
                        secretKey: 'minioadmin'
                    });
                    const suffix = file.name.match(/\.(.+)$/)[1];
                    const fileName = new Date().getTime() + "." + suffix;
                    const bucketName = "images-prod";
                    minioClient.presignedPutObject(bucketName, fileName, 1000 * 60, (err, url) => {
                        if(err){
                            return console.error(err);
                        };
                        axios(url, {
                            method: 'PUT',
                            data: file,
                            timeout: 1800000,
                            headers: {
                                'Content-Type': 'application/octet-stream'
                            },
                            onUploadProgress: (progressEvent) =>{
                                const {loaded, total} = progressEvent;
                                const percent = Math.round((loaded * 100) / total);
                                // that.files[that.files.length - 1].percent = percent;
                                if(percent == 100){
                                    const image = new Image();
                                    const url = "http://192.168.202.52:19000/"+bucketName+"/"+fileName;
                                    image.src = url
                                    image.onload = () => {
                                        const timestamp = new Date().getTime();
                                        const id = "map" + timestamp;
                                        const addModel = {src: url, id: id, clazz: 'map', size: [image.width, image.height], x: 0, y:0};
                                        addModel.type = getShapeName("map");
                                        graph.add('node', {
                                            ...addModel,
                                        });
                                    }
                                }
                            }
                        });
                    });
                    // reader.readAsDataURL(file);
                    // reader.addEventListener('loadend', (e)=>{
                    //     const image = new Image();
                    //     image.src = e.target.result;
                    //     image.onload = () => {
                    //         const src = e.target.result;
                    //         const timestamp = new Date().getTime();
                    //         const id = "map" + timestamp;
                    //         const addModel = {src: src, id: id, clazz:'map', size: [image.width, image.height], x: 0, y:0}
                    //         addModel.type = getShapeName("map");
                    //         graph.add('node', {
                    //             ...addModel,
                    //         });
                    //     }
                    // })
                })
            }
        });

        cmdPlugin.registerCommand('save', {
            queue: false,
            execute: function(graph){
                const map = graph.map;
                const snapshot = graph.save();
                const maps = snapshot.nodes.filter(item => item.clazz === 'map')[0];
                const width = -maps.size[0]/2;
                const height = -maps.size[1]/2;
                const deviationX = parseInt(width - maps.x);
                const deviationY = parseInt(height - maps.y);
                console.log("x轴偏差值", deviationX);
                console.log("y轴偏差值", deviationY);
                cmdPlugin._validate(graph).then(()=>{
                    const snapshot = graph.save();
                    console.log(snapshot)
                    const param = {
                                id: map.id,
                                name: map.name,
                                type: map.type,
                                remark: map.remark,
                                nodes: snapshot.nodes.filter(item => item.clazz !== 'point' && item.clazz !== 'map').map(item => {
                                return {...item, id: null, uuid:item.id, size: item.size?.join(","), 
                                    locationLock: item.locationLock == "1" ? true : false,
                                    x: item.x + deviationX,
                                    y: item.y + deviationY
                                }}),
                                // routeNods: snapshot.nodes.filter(item => item.clazz === 'point')
                                //     .map(item => {
                                //         return {...item, uuid: item.id}}
                                //     ),
                                // edges: snapshot.edges.map(item => {
                                //     return {...item, uuid: item.id,
                                //         controlPoints: JSON.stringify(item.controlPoints),
                                //         startPoint: JSON.stringify(item.startPoint),
                                //         endPoint: JSON.stringify(item.endPoint),
                                //         isDash: item.isDash ? '1': '0',
                                //         isArrow: item.isArrow ? '1' : '0'
                                //     }})
                    };
                    console.log(param);
                    saveMap(param).then(res => {
                        res && message.success("保存成功");
                        const search = window.location.search
                        if(!search){
                            let params = new URLSearchParams(search);
                            // 修改某个参数的值
                            const query = Base64.encode(JSON.stringify({id: res.data.id}));
                            for(let i=0; i<query.length; i++){
                                params.set(i, query[i]);
                            }
                            // // 生成新的 URL
                            let newUrl = window.location.origin + window.location.pathname + '?' + params.toString();
                            // // 修改地址栏
                            window.history.replaceState(null, null, newUrl);
                            graph.map.id = res.data.id;
                        }
                    })
                }).catch(e => {
                    message.error(e);
                })
            }
        })

        cmdPlugin.registerCommand('drawMap', {
            queue: false,
            locked: function(graph){
                return graph.drawRoute;
            },
            execute: function (graph) {
                if(!graph.map || !graph.map.id){
                    message.error("请先保存地图");
                    return
                }
                graph.drawRoute = !graph.drawRoute;
                graph.drawRoute && (graph.routeCode = cmdPlugin._uuid());
                if(!graph.drawRoute){
                    Modal.confirm({
                        title: '保存路线',
                        okText: '确认',
                        icon: () => {return },
                        cancelText: '取消',
                        content: (
                            <div style='display: flex'>
                                <span style='white-space: nowrap; line-height: 30px'><span> * </span>路线名：</span>
                                <Input onchange={(e)=> graph.routeName = e.target.value} />
                            </div>
                        ),
                        onCancel() {
                            graph.drawRoute = true;
                        },
                        onOk() {
                            const snapshot = graph.save();
                            console.log(snapshot);
                            if(graph && !graph.routeName){
                                message.error("路线名称不能为空");
                                return true;
                            }
                            const param = {
                                name: graph.routeName,
                                code: graph.routeCode,
                                mapId: graph.map.id,
                                nodes: snapshot.nodes.filter(item => item.clazz === 'point')
                                    .map(item => {
                                        return {...item, uuid: item.id, locationHeight: item.locationHeight.toFixed(2), routeCode: graph.routeCode}}
                                        ),
                                edges: snapshot.edges.map(item => {
                                    return {...item, uuid: item.id,
                                        routeCode: graph.routeCode,
                                        controlPoints: JSON.stringify(item.controlPoints),
                                        startPoint: JSON.stringify(item.startPoint),
                                        endPoint: JSON.stringify(item.endPoint),
                                        isDash: item.isDash ? '1': '0',
                                        isArrow: item.isArrow ? '1' : '0'
                                }})
                            }
                            saveRoute(param).then(res => {
                                res && message.success("保存成功");
                                graph.emit('clearOrigin');
                            })
                        }
                    });
                }
            }
        })
    }

    /**
     * 提交检查
     * @param graph
     * @private
     */
    _validate(graph){
        return new Promise((resolve, reject) => {
            if(!graph.map || !graph.map.name){
                return reject("地图名称不能为空")
            }
            if(!graph.map || !graph.map.type){
                return reject("请选择地图类型")
            }
            return resolve();
        })
    }

    _uuid() {
        let s = [];
        let hexDigits = "0123456789abcdef";
        for (let i = 0; i < 32; i++) {
            s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
        }
        s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
        s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
        s[8] = s[13] = s[18] = s[23];
        let uuid = s.join("");
        return uuid;
    }
}

export default Command;
